/*
 * Created by BAC IT Deustche Bank
 * User: Sergio Vasquez
 * Date: 5/12/2009
 * Time: 12:56 PM
 * 
 * Description:
 * 
 */
using System;
using System.Collections.Generic;
using System.Threading;
using IScheduler.Core.Schedule.Plan;
using IScheduler.Core.Schedule.Task;

namespace IScheduler.Core.Schedule
{
	/// <summary>
	/// Description of ScheduleTimer.
	/// </summary>
	public class ScheduleTimer
	{
		private Timer timer;
		private DateTime next;
		
		private object syncLock = new object();
		 
		public SchedulePlan SchedulePlan { get; private set; }
        public ICollection<IScheduleTask> ScheduleTasks { get; private set; }
        
        public bool IsEmail { get; set; }

        public string ExtraInfo { get; set; }
        
		public ScheduleTimer()
		{
            this.ScheduleTasks = new List<IScheduleTask>();
            this.SchedulePlan = new SchedulePlan();
		}

        /// <summary>
        /// Is the Task running?
        /// </summary>
        public bool IsRunning
        {
            get { return this.timer != null; }
        }
        
        /// <summary>
        /// Has the tasks completed?
        /// </summary>
        /// <returns></returns>
        public bool HasCompleted()
        {
			foreach (IScheduleTask task in ScheduleTasks)
			{
				if (task.Status != TaskStatus.Completed && 
				    task.Status != TaskStatus.Error &&
				    task.Status != TaskStatus.Stopped)
				{
					return false;
				}
			}
			return true;
        }
        

        /// <summary>
        /// Start schedule timer execution.
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            lock (syncLock)
            {
                if (this.timer != null) return false;

                foreach (IScheduleTask Task in this.ScheduleTasks)
                    Task.Start();

                timer = new Timer(TimerCallback);
                ScheduleNextExecution(true);
                return true;
            }
        }
        /// <summary>
        /// Stop schedule timer execution.
        /// </summary>
        /// <returns></returns>
        public bool Stop()
        {
            lock (syncLock)
            {
                if (this.timer == null) return false;

                foreach (IScheduleTask Task in this.ScheduleTasks)
                    Task.Dispose();

                this.timer.Dispose();
                this.timer = null;
                return true;
            }
        }

        /// <summary>
        /// Compute next schedule execution.
        /// </summary>
        /// <param name="first"></param>
        private void ScheduleNextExecution(bool first)
        {
            DateTime now = DateTime.Now;
            
            next = this.SchedulePlan.ComputeNextExecution(first ? now : next, first);
          
            timer.Change(next.Subtract(now), TimeSpan.FromMilliseconds(-1));
        }

        /// <summary>
        /// Dispose Schedule timer execution.
        /// </summary>
        public void Dispose()
        {
            Stop();
        }

        /// <summary>
        /// Execute TimerCallback when the thread is fired.
        /// </summary>
        /// <param name="state"></param>
        private void TimerCallback(object state)
        {
            double correction = DateTime.Now.Subtract(next).TotalMinutes;
            if (correction < -1 || correction > 1)
            {
                ScheduleNextExecution(true);
            }
            else
            {
                foreach (IScheduleTask Task in this.ScheduleTasks)
                    ThreadPool.QueueUserWorkItem(TaskExecutionCallback, Task);

                ScheduleNextExecution(false);
            }
        }

        /// <summary>
        /// Call Task execution.
        /// </summary>
        /// <param name="state"></param>
        private void TaskExecutionCallback(object state)
        {
            IScheduleTask Task = (IScheduleTask)state;
            try
            {
                Task.Execute();
            }
            catch
            {

            }
        }
	}
}
